<?php
/**
 * Created by PhpStorm.
 * User: xtliy
 * Date: 2018/4/25
 * Time: 11:57
 */

namespace Liyq\ApiDocs\Generators;


use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Routing\Route;
use Illuminate\Support\Arr;
use phpDocumentor\Reflection\DocBlockFactory;
use ReflectionClass;

class LaravelGenerators
{
    public function getUri(Route $route) {
        return $route->uri();
    }

    public function getMethods(Route $route) {
        return $route->methods();
    }

    public function getMiddleware(Route $route) {
        return collect($route->gatherMiddleware())->map(function ($middleware) {
            return $middleware instanceof Closure ? 'Closure' : $middleware;
        })->implode(',');
    }

    public function processRoute(Route $route, $bindings = [], $withResponse = true) {
        $routeAction = $route->getAction();
        $routeGroup = $this->getRouteGroup($routeAction['uses']);
        $routeDescription = $this->getRouteDescription($routeAction['uses']);

        $data = [
            'id' => md5($this->getUri($route) . ':' . implode($this->getMethods($route))),
            'resource' => $routeGroup,
            'title' => $routeDescription ? $routeDescription['summary'] : '',
            'description' => $routeDescription ? $routeDescription['description'] : '',
            'methods' => $this->getMethods($route),
            'middleware' => $this->getMiddleware($route),
            'uri' => $this->getUri($route),
            'parameters' => [],
            'params' => $routeDescription ? $routeDescription['params'] : [],
            'return' => $routeDescription ? $routeDescription['return'] : [],
        ];
        $data = $this->getParameters($data, $routeAction['uses']);
        return $data;
    }

    private function getRouteDescription($uses) {
        list($class, $method) = explode('@', $uses);
        $reflection = new ReflectionClass($class);
        $reflectionMethod = $reflection->getMethod($method);
        $comment = $reflectionMethod->getDocComment();
        if (!$comment) {
            return false;
        }
        $phpdoc = DocBlockFactory::createInstance()->create($comment);
//
        return [
            'summary' => $phpdoc->getSummary(),
            'description' => $phpdoc->getDescription()->render(),
            'params' => $phpdoc->getTagsByName('param'),
            'return' => $phpdoc->getTagsByName('return'),
        ];

    }

    private function getParameters($data, $routeAction) {
        $data['params'] = $this->formatParam($data['params']);
        list($class, $method) = explode('@', $routeAction);
        $reflection = new ReflectionClass($class);
        $reflectionMethod = $reflection->getMethod($method);
        //遍历方法的参数
        foreach ($reflectionMethod->getParameters() as $parameter) {
            //获取参数类型
            $parameterType = $parameter->getClass();
            $parameterData = Arr::exists($data['params'], $parameter->name) ? $data['params'][$parameter->name] : ['unknown', ''];
            //判断类型是否存在
            if (!is_null($parameterType) && class_exists($parameterType->name)) {
                //获取类名
                $className = $parameterType->name;

                if (is_subclass_of($className, FormRequest::class)) {
                    //判断是否为表单验证
                    $parameterReflection = new $className;
                    if (method_exists($parameterReflection, 'validator')) {
                        $rules = $parameterReflection->validator()->getRules();
                    } else {
                        $rules = $parameterReflection->rules();
                    }
                    $attributes = $parameterReflection->attributes();

                    foreach ($rules as $key => $rule) {
                        $description = isset($attributes[$key]) ? $attributes[$key] : $key;
                        $data['parameters'][] = $this->addParameter($key, 'string', $description);
                    }

                } else if (is_subclass_of($className, Model::class)) {
                    //判断是否为模型类型
                    $data['parameters'][] = $this->addParameter($parameter->name, 'int', $parameterData[1]);
                } else {
                    // 除表单请求、模型类型外
                    $data['parameters'][] = $this->addParameter($parameter->name, $parameterData[0], $parameterData[1]);
                }
            } else {
                // 未知的类型
                $data['parameters'][] = $this->addParameter($parameter->name, $parameterData[0], $parameterData[1]);
            }
        }
        return $data;
    }

    private function addParameter($key, $type, $description = '') {
        return [
            'key' => $key,
            'type' => $type,
            'description' => $description
        ];
    }

    private function getRouteGroup($uses) {
        list($class, $method) = explode('@', $uses);
        $reflection = new ReflectionClass($class);
        $comment = $reflection->getDocComment();
        if ($comment) {
            $phpdoc = DocBlockFactory::createInstance()->create($comment);
            return $phpdoc->getSummary();
        }
        return $class;
    }

    private function formatParam($params) {
        $data = [];
        foreach ($params as $param) {
            $data[$param->getVariableName()] = [
                $param->getType(),
                $param->getDescription()
            ];
        }
        return $data;
    }
}